package basics;

import java.util.Arrays;
import java.util.List;
import java.util.Vector;

public abstract class aFeatures implements VectorMatrix {

	public aFeatures minusCopy(aFeatures x) {
		return this.addCopy(x.times(-1));
	}

	public aFeatures addCopy(aFeatures x) {
		aFeatures a = newInstance(x.length());
		sum(a, this, x);
		return a;
	}

	public aFeatures timesCopy(double d) {
		return entrywiseProduct(this, d);
	}

	public aFeatures timesCopy(aFeatures x) {
		return entrywiseProduct(this, x);
	}

	public abstract double get(int index);

	public abstract int length();

	public abstract void set(int index, double v);

	protected abstract aFeatures newInstance(int n);

	public aFeatures sum(aFeatures a, aFeatures b) {
		aFeatures v = newInstance(a.length());
		sum(v, a, b);
		return v;
	}

	protected static void sum(aFeatures v, aFeatures a, aFeatures b) {
		for (int i = 0; i < a.length(); i++) {
			v.set(i, a.get(i) + b.get(i));
		}
	}

	public aFeatures add(aFeatures b) {
		sum(this, this, b);
		return this;
	}

	public void add(double d) {
		for (int i = 0; i < this.length(); i++) {
			this.set(i, this.get(i) + d);
		}
	}

	public aFeatures entrywiseProduct(aFeatures a, aFeatures b) {
		aFeatures v = newInstance(a.length());
		entrywiseProduct(a, b, v);

		return v;
	}

	protected static void entrywiseProduct(aFeatures v, aFeatures a, aFeatures b) {
		for (int i = 0; i < a.length(); i++) {
			v.set(i, a.get(i) * b.get(i));
		}
	}

	public aFeatures entrywiseProduct(aFeatures b) {
		entrywiseProduct(this, this, b);
		return this;
	}

	public aFeatures entrywiseProduct(aFeatures a, double b) {
		aFeatures v = newInstance(a.length());
		entrywiseProduct(v, a, b);
		return v;
	}

	protected static void entrywiseProduct(aFeatures v, aFeatures a, double b) {
		for (int i = 0; i < a.length(); i++) {
			v.set(i, a.get(i) * b);
		}
	}

	public aFeatures entrywiseProduct(double a) {
		entrywiseProduct(this, this, a);
		return this;
	}

	public aFeatures entrywiseProductCopy(aFeatures x) {
		aFeatures a = newInstance(x.length());
		entrywiseProduct(a, this, x);
		return a;
	}

	public aFeatures times(double d) {
		aFeatures a = newInstance(this.length());
		entrywiseProduct(a, this, d);
		return a;
	}

	protected static double sumOfEntryProduct(aFeatures a, aFeatures b) {
		double sum = 0;
		for (int i = 0; i < a.length(); i++) {
			sum += (a.get(i) * b.get(i));
		}

		return sum;
	}

	public abstract aFeatures times(aFeatures x);

	public void set(double d) {
		for (int i = 0; i < this.length(); i++) {
			this.set(i, d);
		}
	}

	@Override
	public String toString() {
		String str = "";
		for (int i = 0; i < length(); i++) {
			str = str + ", " + get(i);
		}
		return str.substring(2) + "\n";
	}

	public aFeatures copy() {
		aFeatures f = newInstance(length());
		for (int i = 0; i < length(); i++) {
			f.set(i, this.get(i));
		}

		return f;
	}

	public static aFeatures product(aFeatures... aFeatures) {
		return product(Arrays.asList(aFeatures));
	}

	public static aFeatures sum(aFeatures... aFeatures) {
		return sum(Arrays.asList(aFeatures));
	}

	public static aFeatures product(List<aFeatures> aFeatures) {
		aFeatures f = aFeatures.get(0);
		for (int i = 1; i < aFeatures.size(); i++) {
			f.times(aFeatures.get(1));
		}

		return f;
	}

	public static aFeatures sum(List<aFeatures> aFeatures) {
		aFeatures f = aFeatures.get(0);
		for (int i = 1; i < aFeatures.size(); i++) {
			f.add(aFeatures.get(1));
		}

		return f;
	}

	public void set(aFeatures a) {
		if (a.length() != length()) {
			new Exception("Not supported at the moment!").printStackTrace();
		}

		for (int i = 0; i < length(); i++) {
			set(i, a.get(i));
		}
	}

	public aFeatures concat(aFeatures a) {
		int n = length() + a.length();
		aFeatures f = newInstance(n);
		for (int i = 0; i < n; i++) {
			if (i < length()) {
				f.set(i, get(i));
			} else {
				f.set(i, a.get(i - length()));
			}
		}

		return f;
	}

	public aFeatures pow(int n) {
		aFeatures ret = this.copy();

		for (int i = 1; i < n; i++) {
			ret.times(this);
		}

		return ret;
	}

	public aFeatures divideCopy(aFeatures a) {
		aFeatures f = newInstance(length());
		for (int i = 0; i < length(); i++) {
			f.set(i, get(i) / a.get(i));
		}
		return f;
	}

	@Override
	public VectorMatrix minus(VectorMatrix m) {
		return minusCopy((aFeatures) m);
	}

	@Override
	public VectorMatrix minusEqual(VectorMatrix m) {
		return minusEqual((aFeatures) m);
	}

	@Override
	public Matrix times(VectorMatrix m) {
		return (Matrix) timesCopy(((aFeatures) m));
	}

	@Override
	public Matrix timesEqual(VectorMatrix m) {
		return (Matrix) times((aFeatures) m);
	}

	@Override
	public VectorMatrix add(VectorMatrix m) {
		return addCopy((aFeatures) m);
	}

	@Override
	public VectorMatrix addEqual(VectorMatrix m) {
		return add((aFeatures) m);
	}

}
